#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
阿里云OSS上传模块
负责将文件上传到阿里云对象存储服务
"""

import os
import logging
import mimetypes
from datetime import datetime
from typing import Dict, Any, Optional, Callable
from pathlib import Path

try:
    import oss2
    OSS_AVAILABLE = True
except ImportError:
    OSS_AVAILABLE = False
    print("警告: 未安装 oss2 库，OSS功能将不可用")
    print("请运行: pip install oss2")


class OSSUploader:
    """阿里云OSS上传器"""

    def __init__(self, config: Dict[str, Any]):
        """
        初始化OSS上传器

        Args:
            config: OSS配置字典，包含access_key_id, access_key_secret, bucket_name, endpoint
        """
        self.logger = logging.getLogger(__name__)

        if not OSS_AVAILABLE:
            self.logger.error("OSS功能不可用：未安装oss2库")
            self.bucket = None
            return

        # 配置验证
        required_fields = ['access_key_id', 'access_key_secret', 'bucket_name', 'endpoint']
        for field in required_fields:
            if not config.get(field):
                self.logger.error(f"OSS配置缺失: {field}")
                self.bucket = None
                return

        try:
            # 初始化OSS客户端
            auth = oss2.Auth(config['access_key_id'], config['access_key_secret'])
            self.bucket = oss2.Bucket(auth, config['endpoint'], config['bucket_name'])

            # 测试连接
            self._test_connection()

            self.logger.info("OSS客户端初始化成功")

        except Exception as e:
            self.logger.error(f"OSS客户端初始化失败: {e}")
            self.bucket = None

    def _test_connection(self) -> bool:
        """测试OSS连接"""
        try:
            if not self.bucket:
                return False

            # 尝试获取bucket信息
            bucket_info = self.bucket.get_bucket_info()
            self.logger.info(f"OSS连接测试成功，Bucket: {bucket_info.name}")
            return True

        except Exception as e:
            self.logger.error(f"OSS连接测试失败: {e}")
            return False

    def upload_file(self,
                   local_path: str,
                   oss_key: Optional[str] = None,
                   folder: str = "videos",
                   progress_callback: Optional[Callable[[int, int], None]] = None) -> Dict[str, Any]:
        """
        上传文件到OSS

        Args:
            local_path: 本地文件路径
            oss_key: OSS中的文件名，如果为None则自动生成
            folder: OSS中的文件夹名
            progress_callback: 进度回调函数 (已上传字节数, 总字节数)

        Returns:
            包含上传结果的字典
        """
        result = {
            "success": False,
            "message": "",
            "local_path": local_path,
            "oss_key": "",
            "oss_url": "",
            "size": 0,
            "upload_time": datetime.now().isoformat()
        }

        try:
            # 检查OSS是否可用
            if not OSS_AVAILABLE or not self.bucket:
                result["message"] = "OSS功能不可用"
                return result

            # 检查本地文件
            if not os.path.exists(local_path):
                result["message"] = f"本地文件不存在: {local_path}"
                return result

            # 获取文件信息
            file_size = os.path.getsize(local_path)
            result["size"] = file_size

            # 生成OSS文件名
            if oss_key is None:
                file_name = os.path.basename(local_path)
                timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
                name, ext = os.path.splitext(file_name)
                oss_key = f"{folder}/{timestamp}_{name}{ext}"
            else:
                if folder and not oss_key.startswith(folder + "/"):
                    oss_key = f"{folder}/{oss_key}"

            result["oss_key"] = oss_key

            # 检测文件类型
            content_type, _ = mimetypes.guess_type(local_path)
            if content_type is None:
                content_type = 'application/octet-stream'

            self.logger.info(f"开始上传文件: {local_path} -> {oss_key}")

            # 上传文件
            if progress_callback:
                # 带进度的上传
                self._upload_with_progress(local_path, oss_key, content_type, progress_callback)
            else:
                # 简单上传
                with open(local_path, 'rb') as f:
                    self.bucket.put_object(oss_key, f, headers={'Content-Type': content_type})

            # 生成访问URL
            oss_url = f"https://{self.bucket.bucket_name}.{self.bucket.endpoint.replace('http://', '').replace('https://', '')}/{oss_key}"
            result["oss_url"] = oss_url

            result["success"] = True
            result["message"] = "上传成功"

            self.logger.info(f"文件上传成功: {oss_url}")

        except Exception as e:
            error_msg = f"上传文件失败: {str(e)}"
            result["message"] = error_msg
            self.logger.error(error_msg)

        return result

    def _upload_with_progress(self,
                             local_path: str,
                             oss_key: str,
                             content_type: str,
                             progress_callback: Callable[[int, int], None]):
        """带进度的文件上传"""

        class ProgressCallback:
            def __init__(self, callback):
                self.callback = callback
                self.bytes_read = 0

            def __call__(self, bytes_read):
                self.bytes_read = bytes_read
                # OSS进度回调传入的是已读取的字节数
                total_size = os.path.getsize(local_path)
                self.callback(bytes_read, total_size)

        # 分片上传大文件（大于100MB）
        file_size = os.path.getsize(local_path)
        if file_size > 100 * 1024 * 1024:  # 100MB
            self._multipart_upload(local_path, oss_key, content_type, progress_callback)
        else:
            # 简单上传
            progress = ProgressCallback(progress_callback)
            with open(local_path, 'rb') as f:
                self.bucket.put_object(
                    oss_key,
                    f,
                    headers={'Content-Type': content_type},
                    progress_callback=progress
                )

    def _multipart_upload(self,
                         local_path: str,
                         oss_key: str,
                         content_type: str,
                         progress_callback: Callable[[int, int], None]):
        """分片上传大文件"""

        # 分片大小 (10MB)
        part_size = oss2.determine_part_size(os.path.getsize(local_path), preferred_size=10 * 1024 * 1024)

        # 初始化分片上传
        upload_id = self.bucket.init_multipart_upload(oss_key).upload_id

        try:
            parts = []
            uploaded_size = 0
            total_size = os.path.getsize(local_path)

            with open(local_path, 'rb') as f:
                part_number = 1
                while True:
                    data = f.read(part_size)
                    if not data:
                        break

                    # 上传分片
                    result = self.bucket.upload_part(oss_key, upload_id, part_number, data)
                    parts.append(oss2.models.PartInfo(part_number, result.etag))

                    # 更新进度
                    uploaded_size += len(data)
                    progress_callback(uploaded_size, total_size)

                    part_number += 1

            # 完成分片上传
            self.bucket.complete_multipart_upload(
                oss_key,
                upload_id,
                parts,
                headers={'Content-Type': content_type}
            )

        except Exception as e:
            # 取消分片上传
            self.bucket.abort_multipart_upload(oss_key, upload_id)
            raise e

    def upload_multiple_files(self,
                             file_paths: list,
                             folder: str = "videos",
                             progress_callback: Optional[Callable[[int, int, str], None]] = None) -> Dict[str, Any]:
        """
        批量上传文件

        Args:
            file_paths: 文件路径列表
            folder: OSS文件夹
            progress_callback: 进度回调 (当前文件索引, 总文件数, 当前文件名)

        Returns:
            批量上传结果
        """
        results = {
            "success": True,
            "message": "",
            "total_files": len(file_paths),
            "uploaded_files": 0,
            "failed_files": 0,
            "file_results": []
        }

        for i, file_path in enumerate(file_paths):
            if progress_callback:
                progress_callback(i, len(file_paths), os.path.basename(file_path))

            result = self.upload_file(file_path, folder=folder)
            results["file_results"].append(result)

            if result["success"]:
                results["uploaded_files"] += 1
            else:
                results["failed_files"] += 1

        if results["failed_files"] > 0:
            results["success"] = False
            results["message"] = f"部分文件上传失败: {results['failed_files']}/{results['total_files']}"
        else:
            results["message"] = f"所有文件上传成功: {results['uploaded_files']}/{results['total_files']}"

        return results

    def delete_file(self, oss_key: str) -> bool:
        """删除OSS文件"""
        try:
            if not self.bucket:
                return False

            self.bucket.delete_object(oss_key)
            self.logger.info(f"删除OSS文件成功: {oss_key}")
            return True

        except Exception as e:
            self.logger.error(f"删除OSS文件失败: {oss_key}, {e}")
            return False

    def file_exists(self, oss_key: str) -> bool:
        """检查OSS文件是否存在"""
        try:
            if not self.bucket:
                return False

            return self.bucket.object_exists(oss_key)

        except Exception as e:
            self.logger.error(f"检查OSS文件存在性失败: {oss_key}, {e}")
            return False

    def get_file_url(self, oss_key: str, expires: int = 3600) -> Optional[str]:
        """
        获取文件访问URL

        Args:
            oss_key: OSS文件名
            expires: URL过期时间（秒），0表示永久URL

        Returns:
            文件访问URL
        """
        try:
            if not self.bucket:
                return None

            if expires > 0:
                # 生成带过期时间的签名URL
                return self.bucket.sign_url('GET', oss_key, expires)
            else:
                # 生成永久URL（需要bucket为公共读）
                return f"https://{self.bucket.bucket_name}.{self.bucket.endpoint.replace('http://', '').replace('https://', '')}/{oss_key}"

        except Exception as e:
            self.logger.error(f"生成文件URL失败: {oss_key}, {e}")
            return None

    def list_files(self, prefix: str = "", max_keys: int = 100) -> list:
        """列出OSS文件"""
        try:
            if not self.bucket:
                return []

            files = []
            for obj in oss2.ObjectIterator(self.bucket, prefix=prefix, max_keys=max_keys):
                files.append({
                    "key": obj.key,
                    "size": obj.size,
                    "last_modified": obj.last_modified,
                    "etag": obj.etag
                })

            return files

        except Exception as e:
            self.logger.error(f"列出OSS文件失败: {e}")
            return []

    def test_oss_connection(self) -> Dict[str, Any]:
        """测试OSS连接状态"""
        result = {
            "success": False,
            "message": "",
            "bucket_info": None
        }

        try:
            if not OSS_AVAILABLE:
                result["message"] = "OSS库未安装"
                return result

            if not self.bucket:
                result["message"] = "OSS客户端未初始化"
                return result

            # 获取bucket信息
            bucket_info = self.bucket.get_bucket_info()
            result["bucket_info"] = {
                "name": bucket_info.name,
                "location": bucket_info.location,
                "creation_date": bucket_info.creation_date,
                "storage_class": bucket_info.storage_class
            }

            result["success"] = True
            result["message"] = "OSS连接正常"

        except Exception as e:
            result["message"] = f"OSS连接失败: {str(e)}"

        return result


if __name__ == "__main__":
    # 测试代码
    logging.basicConfig(level=logging.INFO)

    # 示例配置（请替换为实际配置）
    test_config = {
        "access_key_id": "your_access_key_id",
        "access_key_secret": "your_access_key_secret",
        "bucket_name": "your_bucket_name",
        "endpoint": "oss-cn-hangzhou.aliyuncs.com"
    }

    uploader = OSSUploader(test_config)

    # 测试连接
    test_result = uploader.test_oss_connection()
    print(f"连接测试: {test_result}")

    # 如果有测试文件，可以测试上传
    # result = uploader.upload_file("test.txt")
    # print(f"上传结果: {result}")